﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using HP.Cachalote.DataModels;
using HP.Cachalote.Services;
using HP.Cachalote.Services.Interfaces;
using HP.Cachalote.Services.Shared;
using HP.Cachalote.Services.Validations;
using HP.Cachalote.WPF.Model;
using HP.Cachalote.WPF.Shared;

namespace HP.Cachalote.WPF.ViewModel
{
    /// <summary>
    /// This class contains properties that a View can data bind to.
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class FinishedTodoItemTreeViewModel : MyViewModelBase
    {
        public const string TodoItemIsUndoneMessageToken = "TodoItemIsUndone";
        public const string DeleteAllDoneItemsConfirmMessageToken = "DeleteAllDoneItemsConfirm";

        public TodoLabel CurrentTodoLabel { get; set; }

        private ITodoItemService _todoItemService;

        /// <summary>
        /// Initializes a new instance of the FinishedTodoItemTreeViewModel class.
        /// </summary>
        public FinishedTodoItemTreeViewModel(IDataService dataService)
            : base(dataService)
        {
            _todoItemService = ServiceFactory.CreateTodoItemService();

            // Load Today's TodoItems by default.
            LoadTodoItemsOfSelectedFilter(TodoItemFilterType.Today);
        }

        /// <summary>
        /// Register all necessary messages. It is called by constructor in base class.
        /// </summary>
        protected override void RegisterMessages()
        {
            base.RegisterMessages();

            RegisterSelectToShowRelatedTodoItemsMessage();
            RegisterRequestSetItemAsDoneMessage();
            RegisterRequestSetItemAsIsUndoneMessage();
        }

        /// <summary>
        /// The <see cref="ShowFinishedTodoItems" /> property's name.
        /// </summary>
        public const string ShowFinishedTodoItemsPropertyName = "ShowFinishedTodoItems";

        private bool _showFinishedTodoItems = false;

        /// <summary>
        /// Sets and gets the ShowFinishedTodoItems property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool ShowFinishedTodoItems
        {
            get
            {
                return _showFinishedTodoItems;
            }

            set
            {
                if (_showFinishedTodoItems == value)
                {
                    return;
                }

                RaisePropertyChanging(ShowFinishedTodoItemsPropertyName);
                _showFinishedTodoItems = value;
                RaisePropertyChanged(ShowFinishedTodoItemsPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="TodoItems" /> property's name.
        /// </summary>
        public const string TodoItemsPropertyName = "TodoItems";

        private ObservableCollection<TodoItemTreeViewItemViewModel> _todoItems =
            new ObservableCollection<TodoItemTreeViewItemViewModel>();

        /// <summary>
        /// Sets and gets the TodoItems property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public ObservableCollection<TodoItemTreeViewItemViewModel> TodoItems
        {
            get
            {
                return _todoItems;
            }

            set
            {
                if (_todoItems == value)
                {
                    return;
                }

                RaisePropertyChanging(TodoItemsPropertyName);
                _todoItems = value;
                RaisePropertyChanged(TodoItemsPropertyName);
            }
        }

        private RelayCommand _showOrHideFinishedTodoItemsCommand;

        /// <summary>
        /// Gets the ShowOrHideFinishedTodoItemsCommand.
        /// </summary>
        public RelayCommand ShowOrHideFinishedTodoItemsCommand
        {
            get
            {
                return _showOrHideFinishedTodoItemsCommand
                    ?? (_showOrHideFinishedTodoItemsCommand = new RelayCommand(
                                          () =>
                                          {
                                              ShowFinishedTodoItems = !ShowFinishedTodoItems;
                                          }));
            }
        }

        private RelayCommand _deleteAllCommand;

        /// <summary>
        /// Gets the DeleteAllCommand.
        /// </summary>
        public RelayCommand DeleteAllCommand
        {
            get
            {
                return _deleteAllCommand
                    ?? (_deleteAllCommand = new RelayCommand(ExecuteDeleteAllCommand,
                                          () => TodoItems.Count > 0));
            }
        }

        private void ExecuteDeleteAllCommand()
        {
            // Show a message to confirm whether user needs to delete the selected label.
            var dialogMessage = new DialogMessage("Delete all finished items?", dialogResult =>
            {
                // This is the CallBack of the DialogMessage.
                if (dialogResult == MessageBoxResult.Yes)
                {
                    try
                    {
                        DeleteFinishedItemsFromDataSource();
                        RemoveFinishedItemsFromCollection();
                    }
                    catch (Exception ex)
                    {
                        RequestDisplayErrorMessage(ex.Message);
                    }
                }
            })
            {
                Button = MessageBoxButton.YesNo,
                Caption = "Delete All Finished Items",
                Icon = MessageBoxImage.Question
            };

            Messenger.Default.Send<DialogMessage>(dialogMessage, DeleteAllDoneItemsConfirmMessageToken);
        }

        /// <summary>
        /// Load all related TodoItems of selected TodoLabel for binding on the UI.
        /// </summary>
        private void LoadTodoItemsOfSelectedTodoLabel()
        {
            var items = from item in _todoItemService.Filter(item => item.Label.ID == CurrentTodoLabel.ID && item.IsDone, true)
                        select ViewModelHelper.CreateNewUnfinishedTodoItemTreeViewItemViewModel(CurrentDataService, item);

            TodoItems = new ObservableCollection<TodoItemTreeViewItemViewModel>(items);
        }

        /// <summary>
        /// Load all related TodoItems of selected filter for binding on the UI.
        /// </summary>
        private void LoadTodoItemsOfSelectedFilter(TodoItemFilterType filter)
        {
            var items = from item in _todoItemService.Filter(item => item.IsDone, true).Where(item =>
                        filter == TodoItemFilterType.All || GeneralUtilities.GetFilterType(item.DueDate) == filter)
                        orderby item.Label.ID ascending, item.DueDate ascending, item.StartTime ascending, item.EndTime ascending
                        select ViewModelHelper.CreateNewUnfinishedTodoItemTreeViewItemViewModel(CurrentDataService, item);

            TodoItems = new ObservableCollection<TodoItemTreeViewItemViewModel>(items);
        }

        /// <summary>
        /// Register the messenger of SelectToShowRelatedTodoItemsMessage to show the TodoItems based on selected TodoLabel or TodoItemFilter.
        /// </summary>
        private void RegisterSelectToShowRelatedTodoItemsMessage()
        {
            Messenger.Default.Register<object>(this, TodoLabelTreeViewModel.SelectToShowRelatedTodoItemsMessageToken, (selectedType) =>
            {
                if (selectedType is TodoLabel)
                {
                    CurrentTodoLabel = selectedType as TodoLabel;

                    LoadTodoItemsOfSelectedTodoLabel();
                }
                else if (selectedType is TodoItemFilterType)
                {
                    var selectedFilter = (TodoItemFilterType)selectedType;

                    LoadTodoItemsOfSelectedFilter(selectedFilter);
                }
            });
        }

        /// <summary>
        /// Get the messenger from TodoItemTreeViewItemViewModel. Insert the TodoItem into finished item collection.
        /// </summary>
        private void RegisterRequestSetItemAsDoneMessage()
        {
            Messenger.Default.Register<TodoItem>(this, TodoItemTreeViewItemViewModel.RequestSetItemAsDoneMessageToken, item =>
            {
                try
                {
                    var newItem = ViewModelHelper.CreateNewUnfinishedTodoItemTreeViewItemViewModel(CurrentDataService, item);
                    InsertSelectedItemIntoCollection(newItem);
                }
                catch (Exception ex)
                {
                    RequestDisplayErrorMessage(ex.Message);
                }
            });
        }

        /// <summary>
        /// Get the messenger from TodoItemTreeViewItemViewModel. Update TodoItem as Undone and remove it from the finished item collection.
        /// </summary>
        private void RegisterRequestSetItemAsIsUndoneMessage()
        {
            Messenger.Default.Register<TodoItem>(this, TodoItemTreeViewItemViewModel.RequestSetItemAsUndoneMessageToken, item =>
            {
                try
                {
                    SetItemAsUndone(item.ID);
                    RemoveSelectedItemFromCollection(item.ID);
                }
                catch (Exception ex)
                {
                    RequestDisplayErrorMessage(ex.Message);
                }
            });
        }

        /// <summary>
        /// Remove the specified TodoItem from collection that used by UI, update the summary info and inform all obervers.
        /// </summary>
        /// <param name="itemID">The specified TodoItem ID.</param>
        private void RemoveSelectedItemFromCollection(int itemID)
        {
            // Get the TodoItemTreeViewModel.
            var selectedItem = (from item in TodoItems where item.TodoItemID == itemID select item).FirstOrDefault();

            if (selectedItem != null)
            {
                // Remove the TodoItemTreeViewModel from the TreeViewItem collection.
                TodoItems.Remove(selectedItem);
            }
        }

        /// <summary>
        /// Insert the selected TodoItem into current collection on the top.
        /// </summary>
        /// <param name="item">The selected TodoItem</param>
        private void InsertSelectedItemIntoCollection(TodoItemTreeViewItemViewModel item)
        {
            TodoItems.Insert(0, item);
        }

        /// <summary>
        /// Set TodoItem as done and update data source.
        /// </summary>
        /// <param name="itemID"></param>
        private void SetItemAsUndone(int itemID)
        {
            // Get the TodoItem from data source.
            var todoItem = _todoItemService.Get(itemID);

            if (todoItem != null)
            {
                // Update the TodoItem as is done in data source.
                todoItem.IsDone = false;

                _todoItemService.Update(todoItem);
            }
        }

        /// <summary>
        /// Delete current all finished TodoItems from data source.
        /// </summary>
        private void DeleteFinishedItemsFromDataSource()
        {
            foreach (var item in TodoItems)
            {
                // Get the TodoItem from data source.
                var todoItem = _todoItemService.Get(item.TodoItemID);

                if (todoItem != null)
                {
                    // Delete the TodoItem from data source.
                    _todoItemService.Delete(todoItem);
                }
            }
        }

        /// <summary>
        /// Remove all finished TodoItems from collection that used by UI.
        /// </summary>
        private void RemoveFinishedItemsFromCollection()
        {
            TodoItems.Clear();
        }
    }
}